'''
文件主要包括了TCP通信协议相关的协议代码
'''

import socket
import threading
from time import sleep
from PyQt5.QtCore import pyqtSignal




class TcpLogic(object):
    '''

    '''
    tcp_sinale_write_info = pyqtSignal(str,int)     #创建文件信号，有str和int两个类型的参数
    tcp_sinale_write_msg  = pyqtSignal(str)          #创建消息信号，有str类型的参数

    def __init__(self):
        '''
        初始化
        '''
        self.tcp_socket =None               #初始话套接字
        self.sever_th =None                 #设置服务端标志位
        self.client_th =None                #设置客户端标志位
        self.client_socket_list =list()     #用于保存：作为服务端时，需要连接服务端的客户端的列表
        self.link_flag = self.NoLink        #用于标记是否开启了连接


    def tcp_sever_start(self,port:int) -> None:         #服务端的开启
        '''
        功能函数，TCP服务端开启的方法
        :param port:端口号
        :return:
        '''
        #socket.AF_INET：IPV4地址族的参数，用于表示使用IPV4的地址
        #socket.SOCK_STREAM：用于流套接字的参数，表示使用TCP协议。
        self.tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #创建套接字，且IPV4的地址簇，使用TCP协议

        # 取消主动断开连接四次握手后的TIME_WAIT状态
        #SOL_SOCKET:表示我们设置的选项为套接字级别的选项
        #SO_REUSEADDR：表示允许在套接字关闭以后立即重新使用同一个端口
        #1：表示启用SO_REUSEADDR选项；
        self.tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #设置套接字同一个端口的复用模式

        #设定套接字为非阻塞式
        self.tcp_socket.setblocking(False)              #设置为非阻塞模式
        self.tcp_socket.bind(("",port))                 #绑定IP号和端口号
        self.tcp_socket.listen(5)                       #最大连接数量
        self.sever_th = threading.Thread(target=self.tcp_server_concurrency)    #开启服务端接收的多线程任务
        self.sever_th.start()               #启动多线程任务

        msg = "TCP服务器正在监听端口：%s\n" % str(port)   #设置消息
        self.tcp_sinale_write_msg.emit(msg)             #释放信号

    def tcp_server_concurrency(self):               #TCP服务端的任务
        '''
        功能函数，供创建线程的方法；
        使用子线程用于监听并创建连接，使主线程可以继续运行，以免无响应
        使用非阻塞式并发用于接收客户端消息，减少系统资源浪费，使软件轻量化
        :return:None
        '''
        while True:
            try:
                client_socket,client_address = self.tcp_socket.accept()     #接收客户端的IP和套接字信息
            except  Exception as ret:
                sleep(0.002)

            else:
                client_socket.setblocking(False)        #非阻塞模式
                # 将创建的客户端套接字存入列表,client_address为ip和端口的元组
                self.client_socket_list.append((client_socket,client_address))      #添加进客户端列表
                msg = f"TCP服务端已连接IP：{client_address[0]}端口{client_address[1]}\n"
                self.tcp_sinale_write_msg.emit(msg)                 #代表有客户端连接成功

            # 轮询客户端套接字列表，接收数据
            for client,address in self.client_socket_list:          #便利所有已经连接的客户端：
                try:
                    recv_msg = client.recv(4096)                    #接收数据，最大为4096个字节
                except Exception as ret:
                    pass
                else:
                    if recv_msg:

                        if '\\' in str(recv_msg):
                            info = recv_msg.decode("gbk")  # 对接收数据的编码
                        else:
                            info = recv_msg.decode("utf-8")  # 对接收数据的编码

                        msg = f"来自IP：{address[0]}端口：{address[1]}:"
                        self.tcp_sinale_write_msg.emit(msg)
                        self.tcp_sinale_write_info.emit(info,self.InfoRec)  #发送数据
                    else:
                        client.close()                              #没有数据就关闭客户端的连接
                        self.client_socket_list.remove(client,address)  #从列表删除该客户端

    def tcp_client_start(self,ip:str,port:int) ->None:          #TCP作为客户端
        '''
        功能函数，TCP客户端连接其他服务端的方法
        :param ip:目标IP地址
        :param port:目标端口
        :return:None
        '''
        self.tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #创建套接字，以IPV4为地址簇，并且使用TCP协议栈

        address = (ip,port)         #将地址和端口打包成元组

        try:
            msg = "正在连接目标服务器....\n"
            self.tcp_sinale_write_msg.emit(msg)
            self.tcp_socket.connect(address)        #创建连接
        except Exception as ret:
            msg = "无法连接目标服务器\n"
            self.tcp_sinale_write_msg.emit(msg)

        else:
            self.client_th  = threading.Thread(target=self.tcp_client_concurrency,args=(address,))  #创建线程任务
            self.client_th.start()      #启动线程任务
            msg = "TCP客户端已连接IP：%s端口：%s\n" % address
            self.tcp_sinale_write_msg.emit(msg)     #发送消息

    def tcp_client_concurrency(self,address) ->None:        #TCP作为客户端的多线程任务
        '''
        功能函数，用于TCP客户端创建子线程的方法，阻塞式接收
        :param address:
        :return:
        '''
        while True:
            if self.tcp_socket is None:
                continue
            recv_msg = self.tcp_socket.recv(4096)       #最大接收数据为4096个字节
            if recv_msg:
                if '\\' in str(recv_msg):
                    info = recv_msg.decode("gbk")  # 对接收数据的编码
                else:
                    info = recv_msg.decode("utf-8")  # 对接收数据的编码
                msg = f"来自IP：{address[0]}端口：{address[1]}:"
                self.tcp_sinale_write_msg.emit(msg)
                self.tcp_sinale_write_info.emit(info,self.InfoRec)      #回显接收数据

            else:
                self.tcp_socket.close()                 #关闭
                msg = "从服务器断开连接\n"
                self.tcp_sinale_write_msg.emit(msg)
                break

    def tcp_send(self,send_info:str) -> None:
        '''
        功能函数，用于TCP服务端和TCP客户端发送消息
        :param send_info:
        :return:
        '''
        try:
            if '\\' in send_info:
                send_info_encoded =send_info.encode("gbk")        #发送数据的编码
            else:
                send_info_encoded =send_info.encode("utf-8")        #发送数据的编码

            if self.link_flag == self.ServerTCP:                #当作为服务端的时候
            # 向所有连接的客户端发送消息
                if self.client_socket_list:                     #如果有客户端已经建立连接了
                    for client,address in self.client_socket_list:  #轮询发送信息
                        client.send(send_info_encoded)

                    msg = "TCP服务端已发送"
                    self.tcp_sinale_write_msg.emit(msg)
                    self.tcp_sinale_write_info.emit(send_info,self.InfoSend)    #回显消息

            if self.link_flag == self.ClientTCP:                #当TCP作为客户端的时候
                self.tcp_socket.send(send_info_encoded)         #发送数据
                msg = "客户端已发送"
                self.tcp_sinale_write_msg.emit(msg)
                self.tcp_sinale_write_info.emit(send_info,self.InfoSend)
        except Exception as ret:
            msg = "发送失败\n"
            self.tcp_sinale_write_msg.emit(msg)

    def tcp_close(self) ->None:
        '''
        功能函数，关闭网络连接的方法
        :return:
        '''
        try:
            if self.link_flag == self.ServerTCP:  # 当作为服务端的时候
                for client, address in self.client_socket_list:  # 轮询给每个连接的客户端发断开连接
                    client.shutdown(socket.SHUT_RDWR)  # 主动关闭
                    client.close()  # 关闭

                self.client_socket_list = list()  # 把已连接的客户端列表重新置为空列表
                self.tcp_socket.close()
                msg = "已断开网络\n"
                self.tcp_sinale_write_msg.emit(msg)

                try:
                    StopThreading.stop_thread(self.sever_th)  # 用于正常退出时，捕获异常
                except Exception as ret:
                    pass

            elif self.link_flag == self.ClientTCP:  # 当作为客户端的时候
                try:
                    self.tcp_socket.shutdown(socket.SHUT_RDWR)  # 直接断开连接
                    self.tcp_socket.close()
                    msg = "已经断开网络\n"
                    self.tcp_sinale_write_msg.emit(msg)
                except Exception as ret:
                    pass

                try:
                    StopThreading.stop_thread(self.client_th)
                except Exception as ret:
                    pass
        except Exception as ret:
            print(ret)

    NoLink = -1
    ServerTCP = 0
    ClientTCP = 1
    InfoSend = 0
    InfoRec = 1


























